<?
/*
	Основной класс для работы с БД.
	
	Создан: 2011-02-01
	Автор: djerom
	Версия: 0.0.0.1 
*/
	class c_dbapi_util
	{
		var $conn = 0;
		var $dbase = '';
		var $queryes = 0;
		
		function c_dbapi_util()
		{
			$this->connect();
		}
		
		// Проверяет подключение к БД. Возвращает код ошибки и ее описание.
		function test()
		{
			global $mvc;
			@$this->conn = mysql_connect($mvc->config['dbhost'], $mvc->config['dbuser'], $mvc->config['dbpass']);
			if (mysql_errno()) return array('status' => mysql_errno(), 'error' => mysql_error());			
			
			@mysql_select_db($mvc->config['dbname'], $this->conn);
			if (mysql_errno()) return array('status' => mysql_errno(), 'error' => mysql_error());
			
			$this->dbase = $mvc->config['dbmame']; // Запомним имя базы для внутреннего использования.
			mysql_unbuffered_query('SET NAMES '.$mvc->config['dbcode']);
			if (mysql_errno()) return array('status' => mysql_errno(), 'error' => mysql_error());
			
			return array('status' => 0, 'error' => '');
		}
		
		// Подключение к БД.
		function connect()
		{
			global $mvc;
			if ($this->conn) @mysql_close($this->conn);
			@$this->conn = mysql_connect($mvc->config['dbhost'], $mvc->config['dbuser'], $mvc->config['dbpass']);
			mysql_select_db($mvc->config['dbname'], $this->conn);
			$this->dbase = $mvc->config['dbmame']; // Запомним имя базы для внутреннего использования.
			//mysql_unbuffered_query('SET NAMES '.$mvc->config['dbcode']); 
			mysql_unbuffered_query('SET NAMES utf8', $this->conn);
		}
		
		// Заменить все вставки {table} в запросе на полное имя таблицы.
		function parse($sql)
		{
			global $mvc;
			return preg_replace('/\{(\w*)\}/i', $mvc->config['dbprefix'].'\1', $sql);
		}
		
		// Экранировать спецсимволы для помещения данных в базу.
		// TODO: mode обработать и делать htmlspecialchars.
		function escape($text, $mode = array('sql', 'html'))
		{
			return mysql_escape_string($text); //mysql_escape_string();
		}
		
		// Возвращает список таблиц в данной БД.
		function list_tables($mask = '', $order = '')
		{
			global $mvc;
			$table = $this->parse($table);
			$sql = 'SELECT table_name as name, table_comment as comment FROM information_schema.tables
				WHERE table_schema="'.$mvc->config['dbname'].'"
				AND table_name LIKE "%'.$mask.'%"';
			if ($order)
				$sql .= ' ORDER BY '.$order;
				
			return $this->get_data($sql);
		}
		
		// Проверить существует ли таблица.
		function table_exists($table)
		{
			global $mvc;
			$table = $this->parse($table);
			$sql = 'SELECT table_name FROM information_schema.tables WHERE table_schema="'.$mvc->config['dbname'].'"';
			$sql .= ' AND table_name="'.$table.'"';
			$r = $this->query($sql);
			$ok = (mysql_num_rows($r) > 0);
			mysql_free_result($r);
			return $ok;
		}
		
		// Получить список полей таблицы. (deprecated Перенсено в c_table_model)
		function get_fields($table, $only_names = false)
		{
			global $mvc;
			$table = $this->parse($table);
			$sql = 'SELECT column_name as `name`, data_type as `type`, character_maximum_length as `len`, 
				column_default as `default`, column_comment as `comment`
				FROM information_schema.columns
				WHERE table_schema="'.$mvc->config['dbname'].'" AND table_name="'.$table.'"';
			$r = $this->query($sql);
			$fields = array();
			while ($a = mysql_fetch_assoc($r))
			{
				if ($only_names) // Вернуть только имена полей.
				{
					$fields[$a['name']] = '';
				}
				else
				{
					$a['type'] = str_replace('bigint', 'int', $a['type']);
					$a['type'] = str_replace('timestamp', 'datetime', $a['type']);
					$fields[$a['name']] = $a;
				}
			}
			mysql_free_result($r);
			
			return $fields;
		}		
		
		/*
		// Установить структуру таблицы. (deprecated Перенесено в  c_table_model)
		function set_fields($table, $fields)
		{
			unset($fields['id']);
			$table = $this->parse($table);
			if (!$this->table_exists($table)) // Таблицы нет - создать.
			{
				$sql = 'CREATE TABLE '.$table.'(id int auto_increment primary key';
				foreach ($fields as $k => $v)
				{
					$sql .= ', '.$v['name'].' '.$v['type'];
					if ($v['type'] == 'varchar')
						$sql .= '('.$v['len'].')';
					$sql .= ' COMMENT "'.$v['comment'].'"';
				}
				$sql .= ')';
				$this->query($sql);
			}
			else // Таблица есть - обновить структуру.
			{
				// 1.Новые поля, 2.измененные поля, 3.удаленные поля.
				$old_fields = $this->get_fields($table);
				unset($old_fields['id']);
				
				foreach($fields as $k => $v)
				{					
					//$key = strtolower($k);
					$sql = '';
					// Поле существует.
					if (isset($old_fields[$k]))
					{
						// Тип поля не совпал.
						if (($v['type'] != $old_fields[$k]['type']) || ($v['len'] != $old_fields[$k]['len']) || ($v['comment'] != $old_fields[$k]['comment'])) 
						{						
							$sql = 'ALTER TABLE '.$table.' CHANGE ';
							$sql .= $v['name'].' '.$v['name'].' '.$v['type'];
							if ($v['type'] == 'varchar') $sql .= '('.$v['len'].')';
							$sql .= ' COMMENT "'.$v['comment'].'"';
						}
					}
					else // Поля нет, добавим его.
					{
						$sql = 'ALTER TABLE '.$table.' ADD ';
						$sql .= $k.' '.$v['type'];
						if ($v['type'] == 'varchar') $sql .= '('.$v['len'].')';
						$sql .= ' COMMENT "'.$v['comment'].'"';
					}					
					if ($sql != '') $this->query($sql);
				}	
				
				// Пробегаем по списку старых полей.
				foreach($old_fields as $k=>$v)
				{
					$sql = '';
					// Старого поля нет в списке новых, значит его надо удалить.
					if (!isset($fields[$k]))
					{
						$sql .= 'ALTER TABLE '.$table.' DROP `'.$k.'`';
						$this->query($sql);
					}
				}			
			}
		}
		*/
		
		// Выполняет запрос и возвращает массив с данными.
		// $first_row_only - вернуть только первую строчку результата.
		function get_data($sql, $first_row_only = false)
		{
			$data = array();
			if ($r = $this->query($sql))
			{
				if ($first_row_only)
				{
					$data = mysql_fetch_assoc($r);
				}
				else
				{
					while($a = mysql_fetch_assoc($r))
					{
						$data[] = $a;
					}
				}
				mysql_free_result($r);
			}
			return $data;
		}
		
		// Количество записей в данном запросе (модифицируя запрос в select count).
		function get_records_count($sql)
		{
			$sql = strtolower($sql);
			$i1 = strpos($sql, 'select');
			$i2 = strpos($sql, 'from');
			$sql = substr($sql, 0, $i1 + 6).' count(*) '.substr($sql, $i2);
			return (int)$this->scalar($sql);
		}
		
		
		// Выполнить запрос и вернуть $page страницу данных(на странице $page_size элементов).
		function get_data_paged($sql, $page_size, $page) // -> array('data' => array(), 'pages' => 10, 'page' => 3, 'page_size' => 25)		
		{
			$res = array('page_size' => $page_size); // Результат работы.
			$res['items_count'] = $this->get_records_count($sql); // Количество элементов в запросе.
			$res['pages_count'] = ceil($res['items_count'] / $page_size); // Количество страниц.
			$res['page'] = min($page, $res['pages_count']); // Реальная текущая страница.
			$res['data'] = $this->get_data($sql.' LIMIT '.$page_size*$res['page'].','.$page_size); // Данные текущей страницы.
			return $res;			
		}

		// Выполнить запрос и получить первое значение его первой строки. [testing]
		function scalar($sql)
		{
			$data = $this->get_data($sql);
			if (sizeof($data) > 0)
			{
				$data = array_values($data[0]);
				return $data[0];
			}
			return 0;
		}
		
		// Получить строку данных из указанной таблицы и указанным id.
		function get_row($table, $id)
		{
			$data = $this->get_data('SELECT * FROM '.$table.' WHERE id='.$id);
			return $data[0];
		}
		
		// Установить данные строки $row в таблицу $table.
		function set_row($table, &$row)
		{
			$sql = '';
			if (!isset($row['id']) || ((int)$row['id'] == 0)) // Добавить запись.
			{
				$sql = 'INSERT INTO '.$table.'(';
				$sql2 = 'VALUES (';
				foreach($row as $k=>$v)
				{
					if ($k != 'id')
					{
						$sql .= $k.', ';
						if (is_array($v)) $v = implode(',', $v);
						$sql2 .= "'".$this->escape($v)."', ";
					}
				}
				$sql = substr($sql, 0, strlen($sql) - 2).')';
				$sql2 = substr($sql2, 0, strlen($sql2) - 2).')';			
				$sql .= ' '.$sql2;
				
			}
			else // ID записи не 0, видимо она уже есть в БД и ее нужно обновить.
			{
				$sql = 'UPDATE '.$table.' SET ';
				foreach($row as $k=>$v)
				{
					if ($k != 'id')
					{
						if (is_array($v)) $v = implode(',', $v);
						$sql .= $k."='".$this->escape($v)."', ";
					}
				}
				$sql = substr($sql, 0, strlen($sql) - 2).' WHERE id='.(int)$row['id'];
			}
			//echo $sql;
			$this->query($sql);
			if (!$row['id']) $row['id'] = mysql_insert_id($this->conn); // Заполним id строки если его не было.
		}
		
		// Заносит данные $data в базу (пакетом). 
		// TODO: Разобраться с _checked.
		function set_data($table, &$data)
		{
			foreach ($data as $k => $v)
			{
				unset($v['_checked']); // Remove system field.
				$this->set_row($table, $v);
				$data[$k]['id'] = $v['id'];
			}
		}		
		
		// Удалить строку данных (привязка по id). $row - строка данных или число(id).
		function del_row($table, $row)
		{
			$sql = 'DELETE FROM '.$this->parse($table).' WHERE id='.(isset($row['id']) ? (int)$row['id'] : (int)$row);
			$this->query($sql);
		}
		
		// Удалить данные пакетом. Ориентируемся по id.
		// TODO: Разобраться с _checked.
		function del_data($table, $data)
		{
			foreach ($data as $k => $v)
			{
				//unset($v['_checked']); // Remove system field.
				$this->del_row($table, $v);
			}
		}
		
		// Выполняет запрос и возвращает указатель на результат.
		// $return - вернуть ли результат работы.
		function query($sql, $return = true)
		{
			global $mvc;
			if (!$this->conn) $this->connect();
			$this->queryes++;
			
			$mvc->message('Query: '.$sql);
			$mvc->bm->add('Run sql query');

			if ($return)
				$r = mysql_query($sql, $this->conn);
			else
				mysql_unbuffered_query($sql, $this->conn);
				
			$mvc->bm->add($sql, array('mark' => '#ddf'));
			
			if (mysql_errno() != 0)
			{
				$mvc->message(mysql_error(), 2);
				return 0;
			}
			elseif ($return)
				return $r;
			else
				return 1;
		}
	}
?>
